/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.web.gwt.form.client.manager;

import java.util.ArrayList;
import java.util.List;

import com.genia.toolbox.web.gwt.basics.client.GwtHelper;
import com.genia.toolbox.web.gwt.basics.client.widget.HyperLinkButton;
import com.genia.toolbox.web.gwt.basics.client.widget.WidgetFactory;
import com.genia.toolbox.web.gwt.form.client.CssNames;
import com.genia.toolbox.web.gwt.form.client.bean.ExecutionDelayable;
import com.genia.toolbox.web.gwt.form.client.bean.ExecutionDelayableListener;
import com.genia.toolbox.web.gwt.form.client.message.Messages;
import com.genia.toolbox.web.gwt.form.client.process.ProcessForm;
import com.genia.toolbox.web.gwt.form.client.value.DataIdentifier;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Controller for a form.
 */
public class FormController
    implements ExecutionDelayableListener
{
  /**
   * a command to enable the buttons.
   */
  private final class EnableButtonCommand
      implements Command
  {
    /**
     * enable the control buttons.
     */
    public void execute()
    {
      setButtonEnabled(true);
    }
  }

  /**
   * The submit button.
   */
  private final transient HyperLinkButton submitButton = WidgetFactory.newHyperLinkButton(Messages.SUBMIT_BUTTON_MESSAGE, CssNames.SUBMIT_BUTTON_CSS_STYLE);

  /**
   * The save button.
   */
  private final transient HyperLinkButton saveButton = WidgetFactory.newHyperLinkButton(Messages.SAVE_BUTTON_MESSAGE, CssNames.SAVE_BUTTON_CSS_STYLE);

  /**
   * the reset button.
   */
  private final transient HyperLinkButton resetButton = WidgetFactory.newHyperLinkButton(Messages.RESET_BUTTON_MESSAGE, CssNames.RESET_BUTTON_CSS_STYLE);

  /**
   * the back button.
   */
  private final transient HyperLinkButton backButton = WidgetFactory.newHyperLinkButton(Messages.BACK_BUTTON_MESSAGE, CssNames.BACK_BUTTON_CSS_STYLE);

  /**
   * the buttons to enable/disable.
   */
  private final transient List<HyperLinkButton> buttons = new ArrayList<HyperLinkButton>();

  /**
   * a command to enable the buttons.
   */
  private final transient EnableButtonCommand enableButtonCommand = new EnableButtonCommand();

  /**
   * the callback command.
   */
  private final transient FormSwitchCallback callback;

  /**
   * the main form manager that handles the form that created this form.
   */
  private transient final FormManager previousFormManager;

  /**
   * the form manager that handles the main form.
   */
  private transient final FormManager mainFormManager;

  /**
   * the panel that will contain the forms.
   */
  private transient final Panel mainPanel;

  /**
   * the url to go back to.
   */
  private transient String backUrl = null;

  /**
   * the panel that will contain the buttons.
   */
  private transient final Panel buttonsPanel;



  /**
   * constructor.
   * 
   * @param mainPanel
   *          the panel that will contain the forms
   * @param buttonsPanel
   *          the panel that will contain the buttons
   * @param formIdentifier
   *          the identifier of the form displayed
   * @param dataIdentifier
   *          the identifier of the values begin displayed
   * @param backUrl
   *          the url to go back to
   */
  public FormController(Panel mainPanel, Panel buttonsPanel, final String formIdentifier, final DataIdentifier dataIdentifier, final String backUrl)
  {
    this(mainPanel, buttonsPanel, null, formIdentifier, dataIdentifier, null);
    this.backUrl = backUrl;
  }



  /**
   * constructor.
   * 
   * @param mainPanel
   *          the panel that will contain the forms
   * @param buttonsPanel
   *          the panel that will contain the buttons
   * @param previousFormManager
   *          the manager that handle the form that created this form
   * @param formIdentifier
   *          the identifier of the form displayed
   * @param dataIdentifier
   *          the identifier of the values begin displayed
   * @param callback
   *          the callback to be called when this form switch back to the
   *          original form
   */
  private FormController(Panel mainPanel, Panel buttonsPanel, final FormManager previousFormManager, final String formIdentifier, final DataIdentifier dataIdentifier, final FormSwitchCallback callback)
  {
    this.mainPanel = mainPanel;
    this.buttonsPanel = buttonsPanel;
    addButton(submitButton);
    addButton(saveButton);
    addButton(resetButton);
    addButton(backButton);
    this.previousFormManager = previousFormManager;
    this.callback = callback;
    this.mainFormManager = new FormManager(this, mainPanel, formIdentifier, dataIdentifier);
    this.mainFormManager.addExecutionDelayableListener(this);
  }



  /**
   * adding a button to the list of buttons to enable/disable.
   * 
   * @param hyperLinkButton
   *          the button to add
   */
  public void addButton(HyperLinkButton hyperLinkButton)
  {
    hyperLinkButton.setEnabled(mainFormManager == null ? false : mainFormManager.isInitialized());
    buttons.add(hyperLinkButton);
  }



  /**
   * removing a button to the list of buttons to enable/disable.
   * 
   * @param hyperLinkButton
   *          the button to remove
   */
  public void removeButton(HyperLinkButton hyperLinkButton)
  {
    buttons.remove(hyperLinkButton);
  }



  /**
   * display a new Form.
   */
  public void init()
  {
    submitButton.addClickListener(new ClickListener()
    {
      public void onClick(final Widget widget)
      {
        submitForm(new Command()
        {

          public void execute()
          {
            goBack();
          }

        }, previousFormManager != null);
      }
    });

    resetButton.addClickListener(new ClickListener()
    {
      public void onClick(final Widget widget)
      {
        resetForm();
      }
    });

    saveButton.addClickListener(new ClickListener()
    {
      public void onClick(final Widget widget)
      {
        submitForm(null, true);
      }
    });

    backButton.addClickListener(new ClickListener()
    {
      public void onClick(final Widget widget)
      {
        goBack();
      }
    });

    displayForm(new Command()
    {

      public void execute()
      {
        mainFormManager.resetForm(false);
      }

    });

  }



  /**
   * The backButton button action.
   */
  public void goBack()
  {
    if (previousFormManager == null) {
      if (backUrl == null) {
        GwtHelper.goBack();
      }
      else {
        GwtHelper.redirect(GwtHelper.getCorrectUlr(backUrl));
      }
    }
    else {
      if (callback != null) {
        callback.switchBack(mainFormManager);
      }
      ProcessForm.addWaitingMessage(mainPanel);
      previousFormManager.getFormController().displayForm(new Command()
      {
        public void execute()
        {
          previousFormManager.displayFormValues();
        }
      });
    }
  }



  /**
   * reset the values of the Form to those re turned by the service for the
   * identifier dataIdentifier.
   */
  public void resetForm()
  {
    mainFormManager.resetForm(true);
  }



  /**
   * switch to the form given in parameters.
   * 
   * @param formIdentifier
   *          the identifier of the form to switch to
   * @param dataIdentifier
   *          the data identifier of the form to switch to
   * @param callback
   *          the callback to be called when the new form switch back to this
   *          form
   */
  public void switchForm(final String formIdentifier, final DataIdentifier dataIdentifier, final FormSwitchCallback callback)
  {
    switchForm(mainPanel, buttonsPanel, formIdentifier, dataIdentifier, callback);
  }



  /**
   * switch to the form given in parameters.
   * 
   * @param mainPanel
   *          the panel that will contain the forms
   * @param buttonsPanel
   *          the panel that will contain the buttons
   * @param formIdentifier
   *          the identifier of the form to switch to
   * @param dataIdentifier
   *          the data identifier of the form to switch to
   * @param callback
   *          the callback to be called when the new form switch back to this
   *          form
   */
  public void switchForm(final Panel mainPanel, final Panel buttonsPanel, final String formIdentifier, final DataIdentifier dataIdentifier, final FormSwitchCallback callback)
  {
    mainFormManager.saveFormValues();
    new FormController(mainPanel, buttonsPanel, mainFormManager, formIdentifier, dataIdentifier, callback).init();
  }



  /**
   * change the state of all the control buttons.
   * 
   * @param enabled
   *          whether to enable or disable the buttons
   */
  private void setButtonEnabled(boolean enabled)
  {
    for (HyperLinkButton button : buttons) {
      button.setEnabled(enabled);
    }
  }



  /**
   * send the current values to the server.
   * 
   * @param callback
   *          the {@link Command} called in case of success.
   * @param updateForm
   *          do we need to update the values of the form
   */
  public void submitForm(final Command callback, final boolean updateForm)
  {
    mainFormManager.submitForm(callback, updateForm);
  }



  /**
   * display the current form.
   * 
   * @param callbak
   *          the callback that is called once the form is displayed. It is used
   *          to fill the form with the current values.
   */
  private void displayForm(final Command callbak)
  {
    buttonsPanel.clear();
    mainFormManager.displayForm(new Command()
    {

      public void execute()
      {
        buttonsPanel.add(submitButton);
        buttonsPanel.add(resetButton);
        buttonsPanel.add(saveButton);
        buttonsPanel.add(backButton);
        if (callbak != null) {
          callbak.execute();
        }
      }
    });
  }



  /**
   * fired when the source {@link ExecutionDelayable} change state.
   * 
   * @param source
   *          the source that changed state
   * @param sourceIsInited
   *          the new state
   * @see ExecutionDelayableListener# stateChanged(ExecutionDelayable, boolean)
   */
  public void stateChanged(ExecutionDelayable source, boolean sourceIsInited)
  {
    if (!sourceIsInited) {
      setButtonEnabled(false);
    }
    else {
      mainFormManager.execute(enableButtonCommand);
    }
  }

}
